package com.ztp.lib.files;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import static java.lang.System.out;

import com.ztp.lib.utils.Utils;

public class FtpExplorer implements DisplayableExplorer{
	static FTPFile BACK = new FTPFile();
	static{
		BACK.setName("...");
		BACK.setTimestamp(new GregorianCalendar());
		BACK.setSize(0);
	}
	static final DisplayableFile FTP_ROOT = new DisplayableFile();
	static{
		FTP_ROOT.absPath="/";
		FTP_ROOT.ext="DIR";
		FTP_ROOT.isDirectory=true;
		FTP_ROOT.name="";
		FTP_ROOT.size="0 B";
		FTP_ROOT.modifyDafe="";
	}
	static final String [] ROOTS = new String[]{FTP_ROOT.absPath};
	
	String server, user,password;
	FTPClient client;
	ArrayList<FTPFile> files =new ArrayList<FTPFile>(); 
	String currWorkingDir;
	
	
	public FtpExplorer(String server, String user, String password) throws SocketException, IOException,Exception{
		this.server = server;
		this.user = user;
		this.password = password;
		client = new FTPClient();
		client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
		client.connect(server);
		//client.setControlKeepAliveReplyTimeout(timeout)
		int reply = client.getReplyCode();
		if(!FTPReply.isPositiveCompletion(reply)){
			String [] msgs = client.getReplyStrings();
			String msg = "";
			for(String s : msgs){
				msg += (s +" ");
			}
			client.disconnect();
			throw new Exception(msg);
		}
		
		if(!client.login(user, password)){
			client.logout();
			String [] msgs = client.getReplyStrings();
			String msg = "";
			for(String s : msgs){
				msg += (s +" ");
			}
			
			throw new Exception(msg);
		}
		
		
		listCurrentDir();
	}
	
	///------------PRIVATE METHODS
	
	private boolean setCurrentDir(String path) throws Exception{
		boolean success = false;
		out.println();
		
		for(int i=0;i<2;i++){
			try{
				if(path.equals("...")){
					success = client.changeToParentDirectory();
				}
				else{
					success = client.changeWorkingDirectory(path);
				}
				currWorkingDir = client.printWorkingDirectory();
			}
			catch(Exception se){
				out.print("i=" + i + " " + se);
				try{
					client.disconnect();
					client.connect(server);
					client.login(user, password);
				}
				catch(Exception ee ){
					throw new Exception(se.getMessage() + ", " +ee.getMessage());
				}
			}
		}
		
		return success;
	}
	
	private void listCurrentDir() throws IOException {
		FTPFile [] fileList = client.listFiles();
		if(!files.isEmpty()){
			files.clear();
		}
		files.add(BACK);
		for(int i=0;i < fileList.length;i++){
			files.add(fileList[i]);
		}
		
		applyDirectoryChangeListener();
	}
	
	///------------LISTENERS
	
	HashSet<DirectoryChangeListener> dirChangeListener = new HashSet<DirectoryChangeListener>();
	
	private void applyDirectoryChangeListener(){
		Iterator<DirectoryChangeListener> it =  dirChangeListener.iterator();
		DisplayableFile curr = new DisplayableFile();
		curr.name = FilenameUtils.getName(currWorkingDir);
		curr.size = "0 B";
		curr.ext = "DIR";
		curr.absPath = currWorkingDir;
		curr.modifyDafe = "";
		curr.isDirectory = true;
		
		while(it.hasNext()){
			it.next().directoryChanged(curr);
		}
	}
	
	private void applyRootChanged(){
		
	}
	
	///------------DisplayableExplorer 
	
	@Override
	public void getFile(int index, DisplayableExplorer explorer) {
		FTPFile f = files.get(index);
		OutputStream os = null;
		CountingOutputStream cos = null;
		
		try {
			os = new FileOutputStream(explorer.getCurrentDirAbsPath() + File.pathSeparator + f.getName());
			cos = new CountingOutputStream(os) {
				@Override
				protected synchronized void beforeWrite(int n) {
					super.beforeWrite(n);
					System.out.println( "Downloaded " + Utils.convertFileSize(getCount()));
				}
			};
			client.retrieveFile(f.getName(), cos);
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			if(cos != null){
				try {
					cos.close();
				} catch (IOException e) {
					System.out.println("FtpExplorer::getFile : error while closin");
				}
			}
		}
	}
	
	@Override
	public DisplayableFile getDisplayableFile(int index) {
		return new DisplayableFile(files.get(index), currWorkingDir);
	}

	@Override
	public void addDirectoryChangeListener(DirectoryChangeListener dcl) {
		dirChangeListener.add(dcl);
	}

	@Override
	public void removeDirectoryChangeListener(DirectoryChangeListener dcl) {
		dirChangeListener.remove(dcl);
	}

	@Override
	public int getSize() {
		return files.size();
	}

	@Override
	public boolean setDirectory(int index) throws Exception {
		boolean success = false;
		FTPFile f = files.get(index);
		if(!f.isDirectory() && !f.getName().equals("...")) return false;
		
		setCurrentDir(f.getName());
		listCurrentDir();
		success = true;
		
		return success;
	}

	@Override
	public String getCurrentDirName() {
		return FilenameUtils.getName(currWorkingDir);
	}

	@Override
	public String getCurrentDirAbsPath() {
		return currWorkingDir;
	}

	@Override
	public boolean tryToChangeDirectory(String path) throws Exception {
		boolean success = false;
		try {
			success = setCurrentDir(path);
			listCurrentDir();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return success;
	}

	@Override
	public DisplayableFile[] getRoots() {
		// TODO Auto-generated method stub
		throw new NullPointerException("TODO");
	}

	@Override
	public boolean setUpDirectory() {
		// TODO Auto-generated method stub
		throw new NullPointerException("TODO");
		
	}

	@Override
	public void changeRoot(DisplayableFile root) {
		// DO NOTHING
	}

	@Override
	public String[] getRootsPath() {
		return ROOTS;
	}

	@Override
	public void changeRoot(String rootPath) {
		// DO NOTHING
	}
}
